<html>
<head>
    <title></title>
</head>
<body>

<script type="text/javascript">
    /*solution #1, the simple version of the bind implementation
     Function.prototype.myBind = function(context) {
     var self = this; // store the outter anonymous function (line below)
     return function() {
     return self.apply(context, arguments);
     }
     };
     var obj = {
     name: 'Ji Wu'
     };

     var func = function() {
     alert(this.name); // Ji Wu
    }.myBind(obj);
     func();
     */
    // solution #2
    Function.prototype.myBind = function() {
        var self = this,
                context = [].shift.call(arguments), // to get the context
        // can also do: Array.prototype.shift.call(arguments);
                args = [].slice.call(arguments); // to convert rest of the arguments elements into array

        /*
         in order to understand these 2 lines above, you will need understand javascript allows an object to "borrow" the method of another object,
         example #1:
         var A = function(name) {
         this.name = name;
         }
         var B = function() {
         A.apply(this, arguments);
         }

         example #2:
         obj1 = {
         name: 'Ji Wu',
         greet: function() {
         alert('Hello ' + this.name);
         }
         };
         obj2 = {
         name: 'Jerry Wang'
         }
         obj1.greet.call(obj2); // Hello Jerry Wang
         */

        return function() {
            return self.apply(context, [].concat.call(args, [].slice.call(arguments))); // to concat arguments passed in from two calls
        }
    }

    var obj = {
        name: 'Ji Wu'
    };

    var func = function(a, b, c, d) {
        alert(this.name); // Ji Wu
        alert([a, b, c, d]); // 1,2,3,4
    }.myBind(obj, 1, 2);
    func(3, 4);

    function no_argument(){
        console.log(arguments);
    }

    no_argument("Jerry");
    // Jerry 2017-02-24 7:14PM 这里的=>就是参数列表的开始
    // const curry = fn => (...args) => fn.bind(null, ...args);

    function curry(fn, ...arg){
        return fn.bind(null, ...arg);
    }

    function jerryPrint(...arg){
        console.log("Jerry 2017-02-24: " + arg);
    }

    var curryResult = curry(jerryPrint, "default1", "default2");
    curryResult("Hello", "World");
    curryResult("Hello2", "World2");

    !(function test(){
    var bi = con.bind({a: 1}, "Jerry");
    bi();
    })(); // result in console: hello: Jerry

    function con(a){
        console.log("hello: " + a);
    }

    /*偏函数（Partial Functions）
Partial Functions也叫Partial Applications，这里截取一段关于偏函数的定义：

Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.
这是一个很好的特性，使用bind()我们设定函数的预定义参数，然后调用的时候传入其他参数即可：*/

  function list() {
    return Array.prototype.slice.call(arguments);
  }

var list1 = list(1, 2, 3); // [1, 2, 3]
  console.log("list: " + list1);
  
  // 预定义参数37
  var leadingThirtysevenList = list.bind(undefined, 37);

  var list2 = leadingThirtysevenList(); // [37]
  console.log("list2: " + list2);
  var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
  console.log("list: " + list3);
</script>
</body>
</html>
